package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"strconv"
	"strings"

	"github.com/Luxurioust/excelize" //用于结束后暂停
	"github.com/golang/glog"         //用于写日志

	termbox "github.com/nsf/termbox-go"
)

var mNeedExcel map[string]string //只需要生产某个表格的文件名字
var sNeedExcel []string          //只需要生产某个表格的切片

var mSheetName map[string]string //只需要生产某个表格的文件名字

//用于结束后暂停
func init() {
	if err := termbox.Init(); err != nil {
		panic(err)
	}
	termbox.SetCursor(0, 0)
	termbox.HideCursor()
}

//默认路径 同级
const (
	excelPath  string = "./excel/outputExcel"
	excelOther string = "./excelOther"
	luaPath           = "./lua"
	jsonPath          = "./clientJson"

	tbHead      = "ltbConf"
	luaFileHead = "/Conf"

	oneSeg = "#"
	twoSeg = "|"

	ASeg  = "A" //为客户端和服务器的表都导
	SSeg  = "S" //为服务器的表
	CSeg  = "C" //为只导客户端
	ASegS = "a" //为客户端和服务器的表都导
	SSegS = "s" //为服务器的表
	CSegS = "c" //为只导客户端
)

//加载 遍历 ./excel 下的所有目录
func loadExcels() {
	//得到文件列表
	files, _ := ioutil.ReadDir(excelPath)
	for _, file := range files {
		if strings.HasPrefix(file.Name(), "|") || strings.Contains(file.Name(), ";") {
			fmt.Println("错误文件 " + file.Name()) //excel错误文件
			continue
		}
		if !strings.HasSuffix(file.Name(), ".xlsx") { //仅限xlsx xls不行
			fmt.Println("请使用.xlsx 文件 " + file.Name())
			continue
		}
		filePath := excelPath + "/" + file.Name()
		//fmt.Println("process:" + filePath)

		var fileSuffix string
		fileSuffix = path.Ext(file.Name())
		//fmt.Println("fileSuffix =", fileSuffix)

		var filenameOnly string
		filenameOnly = strings.TrimSuffix(file.Name(), fileSuffix)
		//fmt.Println("filenameOnly =", filenameOnly)

		//onceFile(filePath, filenameOnly) //打开单个文件

		if len(sNeedExcel) == 0 { //如果没有参数 所有的表都生成
			onceFile(filePath, filenameOnly) //打开单个文件
		} else { //如果有参数 只生成哈希表里面存在的
			// ==========================================
			// 查找键值是否存在
			if _, ok := mNeedExcel[filenameOnly]; ok {
				onceFile(filePath, filenameOnly) //打开单个文件
				delete(mNeedExcel, filenameOnly) //找到就删除
			} else {

			}
		}
	}

	ofiles, _ := ioutil.ReadDir(excelOther)
	for _, file := range ofiles {
		if strings.HasPrefix(file.Name(), "|") || strings.Contains(file.Name(), ";") {
			fmt.Println("错误文件 " + file.Name()) //excel错误文件
			continue
		}
		if !strings.HasSuffix(file.Name(), ".xlsx") { //仅限xlsx xls不行
			fmt.Println("请使用.xlsx 文件 " + file.Name())
			continue
		}
		filePath := excelOther + "/" + file.Name()
		//fmt.Println("process:" + filePath)

		//onceFile(filePath, filenameOnly) //打开单个文件

		var fileSuffix string
		fileSuffix = path.Ext(file.Name())
		//fmt.Println("fileSuffix =", fileSuffix)

		var filenameOnly string
		filenameOnly = strings.TrimSuffix(file.Name(), fileSuffix)
		//fmt.Println("filenameOnly =", filenameOnly)

		if len(sNeedExcel) == 0 {
			onceFile(filePath, filenameOnly) //打开单个文件
		} else {
			// ==========================================
			// 查找键值是否存在
			if _, ok := mNeedExcel[filenameOnly]; ok {
				onceFile(filePath, filenameOnly) //打开单个文件
				delete(mNeedExcel, filenameOnly) //找到就删除
			} else {

			}
		}

	}

	//剩下的都是填写错误的
	for fileName := range mNeedExcel {
		fmt.Println("文件 [" + mNeedExcel[fileName] + "] 找不到 请检查bat里面填入的文件名 ")
		glog.Info("文件 [" + mNeedExcel[fileName] + "] 找不到 请检查bat里面填入的文件名 ")
	}
}

func onceFile(filePath string, filenameOnly string) {
	xlsx, err := excelize.OpenFile(filePath)
	if err != nil {
		return
	}
	for _, name := range xlsx.GetSheetMap() {
		//fmt.Println(index, name)
		if filenameOnly == name { //叶峰说与文件名一致就解析
			onceSheet(xlsx, name, filePath) //打开一个Sheet
		}
	}
}

func onceSheet(xlsx *excelize.File, sheetName string, filePath string) {

	//获得整个表的数组(其实go里面叫切片)
	rows := xlsx.GetRows(sheetName)

	// if !strings.Contains(sheetName, "|") {
	// 	return
	// }
	fmt.Println("process:" + sheetName)
	glog.Info("process:" + sheetName)

	// 查找键值是否存在
	if _, ok := mSheetName[sheetName]; ok {
		fmt.Println("发现重复表[文件1名称]: " + filePath + "[文件2名称]: " + mSheetName[sheetName] + " [标签名称]: " + sheetName)
		glog.Info("发现重复表[文件1名称]: " + filePath + "[文件2名称]: " + mSheetName[sheetName] + " [标签名称]: " + sheetName)
	} else {
		mSheetName[sheetName] = filePath
	}

	// if "建筑|Building" == sheetName {
	// 	fmt.Println("process:" + sheetName)
	// }

	// sheetNameArr := strings.Split(sheetName, "|")
	// tbName := sheetNameArr[1]
	tbName := sheetName

	//获得 S L SL数据 L代表客户端 S代表服务器 SL代表客户端服务器共有数据
	slType := rows[2]
	//	fmt.Print(slType)

	//获得表类型数据 int string table
	dataType := rows[1]
	//	fmt.Print(dataType)

	//获得表类型数据 sn s_name
	dataName := rows[3]
	//	fmt.Print(dataName)

	var szLua = ""
	//szLua += getLuaColKey(slType, dataName)

	/*
		//snKey = {[1001]=1,[1002]=2,[1003]=3},
		szLua += "\tsnKey = {"
		for rowK, row := range rows {
			if rowK == 0 || rowK == 1 || rowK == 2 || rowK == 3 {
				continue
			}
			if len(row[0]) <= 0 { //会出现空串的情况就不导出
				continue
			}
			szLua += getLuaSnKey(rowK, row, slType, dataName)
			//fmt.Println(rowK)
		}
		szLua += "},\n"
	*/

	//用于计算多余空行去掉最后一个逗号
	var nilRowNums = 0
	for _, row := range rows {
		if len(row[0]) <= 0 { //会出现空串的情况就不导出
			nilRowNums++
		}
	}

	for rowK, row := range rows {
		if rowK == 0 || rowK == 1 || rowK == 2 || rowK == 3 {
			continue
		}
		if len(row[0]) <= 0 { //会出现空串的情况就不导出
			continue
		}
		szLua += getLuaRowsString(rowK, row, slType, dataType, dataName, sheetName)
		//fmt.Println(rowK)

		//fmt.Print(len(rows))
		//fmt.Print(rowK + 1)
		if rowK+1 < len(rows)-nilRowNums {
			szLua += ","
		}

		szLua += "\n"
	}

	final := ""
	// final += tbHead
	// final += tbName
	final += "[\n"
	final += szLua
	final += "\n]\n\n"
	//final += getSuffixFun(tbHead + tbName)

	writeFileName := jsonPath + luaFileHead + tbName + ".json"
	ioutil.WriteFile(writeFileName, []byte(final), 0666)
}

func getSuffixFun(tableName string) string {
	var szLua = ""

	szLua = szLua + tableName + ".get = function( sn )\n"
	szLua = szLua + "	local liMax = #" + tableName + "\n"
	szLua = szLua + "	for i = 1, liMax do\n"
	//szLua = szLua + "		if sn == " + tableName + "[i].sn then\n"
	szLua = szLua + "		if sn == " + tableName + "[i].sn then\n"
	szLua = szLua + "			return " + tableName + "[i]\n"
	szLua = szLua + "		end\n"
	szLua = szLua + "	end\n"
	szLua = szLua + "\n"
	szLua = szLua + "	return nil\n"
	szLua = szLua + "end\n"
	szLua = szLua + "\n"
	szLua = szLua + "" + tableName + ".getBy = function( ... )\n"
	szLua = szLua + "	local ltbCondition = { }\n"
	szLua = szLua + "	local ltbResult = { }\n"
	szLua = szLua + "\n"
	szLua = szLua + "	local arg = { ... }\n"
	szLua = szLua + "	for k, v in ipairs( arg ) do\n"
	szLua = szLua + "		ltbCondition[k] = v\n"
	szLua = szLua + "	end\n"
	szLua = szLua + "\n"
	szLua = szLua + "	local liMax		= table.maxn( ltbCondition )\n"
	szLua = szLua + "	local liMaxTb	= table.maxn( " + tableName + " )\n"
	szLua = szLua + "	if liMax % 2 ~= 0 then\n"
	szLua = szLua + "		return nil\n"
	szLua = szLua + "	end\n"
	szLua = szLua + "\n"
	szLua = szLua + "	local liIndex = 1\n"
	szLua = szLua + "	for liIndex = 1, liMaxTb do\n"
	szLua = szLua + "		local lbIsMatch = true\n"
	szLua = szLua + "\n"
	szLua = szLua + "		for j = 1, liMax, 2 do\n"
	szLua = szLua + "			local lstrKey = ltbCondition[j]\n"
	szLua = szLua + "			local lVaule = ltbCondition[j+1]\n"
	szLua = szLua + "\n"
	szLua = szLua + "			if " + tableName + "[liIndex][lstrKey] ~= lVaule then\n"
	szLua = szLua + "				lbIsMatch = false\n"
	szLua = szLua + "				break\n"
	szLua = szLua + "			end\n"
	szLua = szLua + "		end\n"
	szLua = szLua + "\n"
	szLua = szLua + "		if lbIsMatch then\n"
	szLua = szLua + "			return " + tableName + "[liIndex]\n"
	szLua = szLua + "		end\n"
	szLua = szLua + "	end\n"
	szLua = szLua + "\n"
	szLua = szLua + "	return nil\n"
	szLua = szLua + "end\n"
	szLua = szLua + "\n"
	szLua = szLua + "" + tableName + ".findAll = function( )\n"
	szLua = szLua + "	return " + tableName + "\n"
	szLua = szLua + "end\n"

	return szLua
}

func typeof(v interface{}) string {
	switch t := v.(type) {
	case int:
		return "int"
	case float64:
		return "float64"
	case string:
		return "string"
	//... etc
	default:
		_ = t
		return "unknown"
	}
}

//整合所有行的key名 去对应一行子数组下标
func getLuaColKey(slType []string, dataName []string) string {
	// colKey = {sn=1, name=2, age=3, phone=4}, --getLuaColKey
	// snKey = {[1001]=1,[1002]=2,[1003]=3},
	// [1] = {1001,"小李",31,18087777777},
	// [2] = {1002,"小叶",33,18908888888},
	// [3] = {1003,"小卢",35,13699999999},
	clientRowLen := 0
	clientRowIdx := 0
	for _, slstr := range slType {
		if strings.Contains(slstr, CSeg) {
			clientRowLen++
		}
	}

	var szLua string
	szLua = szLua + "\tcolKey={"
	for colKey, keyName := range dataName {
		if slType[colKey] == CSeg || slType[colKey] == ASeg {
			clientRowIdx++
			szLua = szLua + keyName + "=" + strconv.Itoa(clientRowIdx)
			if clientRowIdx < clientRowLen {
				szLua += ","
			}
		}
	}
	//	fmt.Print(szLua)
	szLua += "},\n"
	return szLua
}

//整合所有行的sn 去对应一行数组下标
func getLuaSnKey(rowK int, row []string, slType []string, dataName []string) string {
	//snKey = {[1001]=1,[1002]=2,[1003]=3},
	//[1001]=1, 该函数完成

	// clientRowLen := 0
	// clientRowIdx := 0
	// for _, slstr := range slType {
	// 	if strings.Contains(slstr, CSeg) {
	// 		clientRowLen++
	// 	}
	// }

	var szLua string
	szLua = szLua + "[" + row[0] + "]=" + strconv.Itoa(rowK-3) + ","
	// szLua = szLua + "\tcolKey={"
	// for colKey, keyName := range dataName {
	// 	if slType[colKey] == CSeg || slType[colKey] == ASeg {
	// 		clientRowIdx++
	// 		szLua = szLua + keyName + "=" + strconv.Itoa(clientRowIdx)
	// 		if clientRowIdx < clientRowLen {
	// 			szLua += ","
	// 		}
	// 	}
	// }

	return szLua
}

//得到一行拼好的有效字符串
func getLuaRowsString(rowK int, row []string, slType []string, dataType []string, dataName []string, sheetName string) string {
	// colKey = {sn=1, name=2, age=3, phone=4}, --getLuaColKey
	// snKey = {[1001]=1,[1002]=2,[1003]=3},
	// [1] = {1001,"小李",31,18087777777}, --getLuaRowsString
	// [2] = {1002,"小叶",33,18908888888},
	// [3] = {1003,"小卢",35,13699999999},
	clientRowLen := 0
	clientRowIdx := 0
	for _, slstr := range slType {
		// if strings.Contains(slstr, CSeg) || strings.Contains(slstr, CSegS) {
		if slstr == CSeg || slstr == CSegS || slstr == ASeg || slstr == ASegS {
			clientRowLen++
		}
	}

	// if slType[0] == CSeg || slType[0] == CSegS || slType[0] == ASeg || slType[0] == ASegS {
	// 	clientRowLen++
	// }

	/*
		var szLua string
		idx := strconv.Itoa(rowK - 3)
		szLua = szLua + "\t[" + idx + "]={"
	*/
	var szLua string
	szLua = szLua + "{"
	for colKey, colCell := range row {
		//		fmt.Print(len(row))
		if slType[colKey] == CSeg || slType[colKey] == ASeg ||
			slType[colKey] == CSegS || slType[colKey] == ASegS {
			clientRowIdx++
			// if dataName[colKey] == "id" || dataName[colKey] == "Id" || dataName[colKey] == "ID" {
			// 	//szLua = szLua + "\t[" + colCell + "]={"
			// 	szLua = szLua + "\"" + dataName[colKey] + "\": " + colCell
			// 	//szLua = szLua + colCell
			// 	if clientRowIdx < clientRowLen {
			// 		szLua += ","
			// 	}

			// } else
			if dataType[colKey] == "int" || dataType[colKey] == "Int" {

				if sheetName != "屏蔽字库|Shield" && sheetName != "全局文本|Guitext" {
					if strings.Contains(colCell, oneSeg) {
						fmt.Println("行数:" + strconv.Itoa(rowK) + " [int] 出现 ~ 请转换为 -> [array] " + dataName[colKey])
						glog.Info("行数:" + strconv.Itoa(rowK) + " [int] 出现 ~ 请转换为 -> [array] " + dataName[colKey])
					}

					if strings.Contains(colCell, twoSeg) {
						fmt.Println("行数:" + strconv.Itoa(rowK) + " [int] 出现 $ 请转换为 -> [arrayex] " + dataName[colKey])
						glog.Info("行数:" + strconv.Itoa(rowK) + " [int] 出现 $ 请转换为 -> [arrayex] " + dataName[colKey])
					}
				}

				//处理默认值的情况 没填 默认0
				if colCell == "" || colCell == "nil" {
					szLua = szLua + "\"" + dataName[colKey] + "\": " + "0"
				} else {
					szLua = szLua + "\"" + dataName[colKey] + "\": " + colCell
				}

				//szLua = szLua + colCell
				if clientRowIdx < clientRowLen {
					szLua += ","
				}

			} else if dataType[colKey] == "string" || dataType[colKey] == "String" {

				// if sheetName != "屏蔽字库|Shield" && sheetName != "全局文本|Guitext" {
				// 	if strings.Contains(colCell, oneSeg) {
				// 		fmt.Println("行数:" + strconv.Itoa(rowK) + " [String] 出现 ~ 请转换为 -> [array] " + dataName[colKey])
				// 		glog.Info("行数:" + strconv.Itoa(rowK) + " [String] 出现 ~ 请转换为 -> [array] " + dataName[colKey])
				// 	}

				// 	if strings.Contains(colCell, twoSeg) {
				// 		fmt.Println("行数:" + strconv.Itoa(rowK) + " [String] 出现 $ 请转换为 -> [arrayex] " + dataName[colKey])
				// 		glog.Info("行数:" + strconv.Itoa(rowK) + " [String] 出现 $ 请转换为 -> [arrayex] " + dataName[colKey])
				// 	}
				// }

				if colCell == "nil" {
					szLua = szLua + "\"" + dataName[colKey] + "\": " + "\"" + "" + "\""
				} else {
					szLua = szLua + "\"" + dataName[colKey] + "\": " + "\"" + colCell + "\""
				}

				//szLua = szLua + "\"" + colCell + "\""
				if clientRowIdx < clientRowLen {
					szLua += ","
				}

			} else if dataType[colKey] == "arrayex" || dataType[colKey] == "Arrayex" ||
				dataType[colKey] == "attr" || dataType[colKey] == "Attr" ||
				dataType[colKey] == "pair" || dataType[colKey] == "pairarray" ||
				dataType[colKey] == "Pair" || dataType[colKey] == "Pairarray" {
				// [key] = 二维数组
				//原格式
				//aaa~1~2~3$aaa~2~2~4
				// 输出格式
				// {
				//   {"aaa",1,2,3},
				//   {"aaa",2,2,4}
				// }
				szTable := "\"" + dataName[colKey] + "\": "
				//szTable := ""
				szTable = szTable + "["
				table := strings.Split(colCell, twoSeg)
				if table[0] != "" {
					if table[0] != "nil" {
						for ak, arr := range table {
							//						fmt.Print(arr)
							//if strings.Contains(colCell, ",") { //所有的全部统一为二维数组
							szTable = szTable + "["
							//} //所有的全部统一为二维数组
							sonArr := strings.Split(arr, oneSeg)
							for k, v := range sonArr {
								_, error := strconv.Atoi(v)
								if error != nil { //str
									szTable = szTable + "\"" + v + "\""
								} else { //int
									szTable = szTable + v
								}
								if k+1 < len(sonArr) {
									szTable += ","
								}
							}
							//if strings.Contains(colCell, ",") { //所有的全部统一为二维数组
							szTable = szTable + "]"
							//} //所有的全部统一为二维数组
							if ak+1 < len(table) {
								szTable += ","
							}
						}
					} else {
						//szTable += "{}"
					}
				}
				szTable = szTable + "]"
				if clientRowIdx < clientRowLen {
					szTable += ","
				}
				szLua = szLua + szTable
			} else if dataType[colKey] == "array" || dataType[colKey] == "Array" {
				if strings.Contains(colCell, ",") {
					fmt.Println("行数:" + strconv.Itoa(rowK) + " [array] 出现 $ 请转换为 -> [arrayex] " + dataName[colKey])
					glog.Info("行数:" + strconv.Itoa(rowK) + " [array] 出现 $ 请转换为 -> [arrayex] " + dataName[colKey])
				}
				// [key] = 一维数组
				//原格式
				//aaa~1~2~3$aaa~2~2~4	--> 报错
				//原格式
				//aaa~1~2~3
				// 输出格式
				// {"aaa",1,2,3},
				szTable := "\"" + dataName[colKey] + "\": "
				//szTable := ""
				szTable = szTable + "["

				sonArr := strings.Split(colCell, oneSeg)
				if sonArr[0] != "nil" {
					for k, v := range sonArr {
						_, error := strconv.Atoi(v)
						if error != nil { //str
							if len([]rune(v)) > 0 {
								szTable = szTable + "\"" + v + "\""
							}
						} else { //int
							szTable = szTable + v
						}
						if k+1 < len(sonArr) {
							szTable += ","
						}
					}
				} else {

				}

				szTable = szTable + "]"
				if clientRowIdx < clientRowLen {
					szTable += ","
				}
				szLua = szLua + szTable

			}
			//			fmt.Print(szLua)
		}

	}
	//	fmt.Print(szLua)
	szLua += "}"

	return szLua
}

//得到一行拼好的有效字符串
// func getJsonRowsString(xlsx *excelize.File, sheetName string) {

// }

//用于结束后暂停
func pause() {
	fmt.Println("请按任意键继续...")
Loop:
	for {
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			break Loop
		}
	}
}

func main() {

	//os.Args 提供原始命令行参数访问功能。注意，切片中的第一个参数是该程序的路径，并且 os.Args[1:]保存所有程序的的参数。
	argsWithoutProg := os.Args[1:]

	if len(argsWithoutProg) > 2 { //如果参数大于2个以上 其中有2个参数是给日志用的
		sNeedExcel = os.Args[3:]
	}

	// 再使用make函数创建一个非nil的map，nil map不能赋值
	mNeedExcel = make(map[string]string)

	// 再使用make函数创建一个非nil的map，nil map不能赋值
	mSheetName = make(map[string]string)

	//将切片转换为哈希表
	for _, fileName := range sNeedExcel {
		mNeedExcel[fileName] = fileName
	}

	//初始化命令行参数
	flag.Parse()

	loadExcels()

	// 参考文章 https://studygolang.com/articles/2275
	// 退出时调用，确保日志写入文件中
	glog.Flush()

	fmt.Println("done")
	pause()
}
